package com.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class ApplicationContext {

	private Class configClass;

	private ConcurrentHashMap<String,Object> singletonObjects=new ConcurrentHashMap<>();//单例池
	private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap=new ConcurrentHashMap<>();//Bean定义

	private List<BeanPostProcessor> beanPostProcessorList=new ArrayList<>();

	public ApplicationContext(Class configClass) {
		this.configClass = configClass;

		scan(configClass);

 		Iterator<String> iterator=beanDefinitionMap.keySet().iterator();
		while (iterator.hasNext()) {
			String beanName=iterator.next();
			BeanDefinition beanDefinition=beanDefinitionMap.get(beanName);
			if(beanDefinition.getScope().equals("singleton")){
				Object bean=createBean(beanDefinition,beanName);
				singletonObjects.put(beanName,bean);
			}
		}
	}


	public Object createBean(BeanDefinition beanDefinition,String beanName){
		Class clazz=beanDefinition.getClazz();
		try {


			Object instance=clazz.getDeclaredConstructor().newInstance();
			//依赖注入
			for (Field declaredField : clazz.getDeclaredFields()) {
				if (declaredField.isAnnotationPresent(Autowired.class)) {
					Object bean=getBean(declaredField.getName());
					declaredField.setAccessible(true);
					declaredField.set(instance,bean);
				}
			}
			//Aware回调
			if(instance instanceof BeanNameAware){
				((BeanNameAware) instance).setBeanName(beanName);
			}

			//初始化前
			for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
				instance=beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
			}

			//初始化
			if(instance instanceof  InitializingBean){
				((InitializingBean) instance).afterPropertiesSet();
			}

			//初始化之后
			for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
				instance=beanPostProcessor.postProcessAfterInitialization(instance,beanName);
			}
			return instance;
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return  null;
	}

	private void scan(Class configClass){
		//解析配置类
		//ComponentScan注解---》扫描路径--》扫描
		ComponentScan componentScan= (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
		String path=componentScan.value();

		ClassLoader classLoader=configClass.getClassLoader();
		URL resource=classLoader.getResource(path.replace(".","/"));
		File file=new File(resource.getFile());
		if (file.isDirectory()) {
			File[] files=file.listFiles();
			for (File file1 : files) {
				//D:\qing\my-spring\target\classes\com\qing\service\UserService.class
				String fileName=file1.getAbsolutePath();
				if(fileName.endsWith(".class")){
					String className=fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));

					//com.qing.service.UserService
					className=className.replace("\\",".");

					Class<?> clazz=null;
					try {
						clazz=classLoader.loadClass(className);

						if(clazz.isAnnotationPresent(Component.class)){

							//特殊类
							if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
								try {
									BeanPostProcessor instance=(BeanPostProcessor)clazz.getDeclaredConstructor().newInstance();
									beanPostProcessorList.add(instance);
								} catch (InstantiationException e) {
									e.printStackTrace();
								} catch (IllegalAccessException e) {
									e.printStackTrace();
								} catch (InvocationTargetException e) {
									e.printStackTrace();
								} catch (NoSuchMethodException e) {
									e.printStackTrace();
								}

							}

							//这个类是个Bean
							//Class->Bean
							//判断bean是单例Bean 还是 prototype Bean
							Component componentAnnotation=clazz.getDeclaredAnnotation(Component.class);
							String beanName=componentAnnotation.value()==""?className:componentAnnotation.value();

							BeanDefinition beanDefinition=new BeanDefinition();
							beanDefinition.setClazz(clazz);
							//类作用域
							if(clazz.isAnnotationPresent(Scope.class)){
								Scope scopeAnnotation=clazz.getDeclaredAnnotation(Scope.class);
								beanDefinition.setScope(scopeAnnotation.value());
							}else{
								beanDefinition.setScope("singleton");
							}
							beanDefinitionMap.put(beanName,beanDefinition);


						}

					} catch (ClassNotFoundException e) {
						e.printStackTrace();
					}

				}

			}
		}
	}

	public Object getBean(String beanName){
		if(beanDefinitionMap.containsKey(beanName)){
			BeanDefinition beanDefinition=beanDefinitionMap.get(beanName);
			if(beanDefinition.getScope().equals("singleton")){
				return  singletonObjects.get(beanName);
			}else{
				//创建Bean对象
				return createBean(beanDefinition,beanName);
			}
		}else{
			//对应bean不存在
			throw new NullPointerException();
		}
	}
}
